package cc.rengu.igas.channel.cpcn.core.packswitch;

import cc.rengu.igas.channel.cpcn.common.constant.CpcnParamConstant;
import cc.rengu.igas.channel.cpcn.common.constant.CpcnTreeNodeConstant;
import cc.rengu.igas.channel.cpcn.common.enums.RespCodeEnum;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.DstChannelCfgMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.DstChannelCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.DstChannelCfg;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.utility.util.RSAUtil;
import cc.rengu.oltp.utility.util.StringUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.apache.commons.codec.binary.Base64;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.ByteArrayInputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.List;
import java.util.Optional;

/**
 * @author wukefan
 * @version 1.0.0
 * @date 2020/7/21 14:35
 */
public class CpcnClientPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {

    RgLogger rglog = RgLog.getLogger(this.getClass().getName());

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        RSAPublicKey publicKey = null;
        String recvData = new String(bytes);
        rglog.debug("recvData  = <{}>", recvData);
        String[] buf = recvData.split(",");
        if (buf.length != 2) {
            rglog.error("返回报文不符合中金支付接口规范！");
            return new byte[0];
        }
        String message = buf[0];
        byte[] recvBuf = Base64.decodeBase64(message);
        String signature = buf[1];
        //验签
        if (StringUtil.isEmptyOrNull(signature)) {
            //获取加密证书
            String secPlanInfoListStr = xmlTreeUtil.getXmlTreeStringValue(CpcnTreeNodeConstant.SEC_PLAN_INFO_LIST);
            rglog.debug("安全计划信息列表  = <{}>", secPlanInfoListStr);
            List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListStr, SecPlanInfo.class);
            Optional<SecPlanInfo> secPlanInfoOptional = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(CpcnParamConstant.SIGN_CERT_TYPE_D)).findFirst();
            rglog.debug("获取证书索引<{}>", secPlanInfoOptional.get().getInstId() + secPlanInfoOptional.get().getSecPlanId() + secPlanInfoOptional.get().getSecPlanType());
            if (secPlanInfoOptional.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(secPlanInfoOptional.get().getInstId() + secPlanInfoOptional.get().getSecPlanId() + secPlanInfoOptional.get().getSecPlanType());
                if (null == certInfo) {
                    rglog.error("证书信息未配置,请先配置证书");
                    throw new BizException(RespCodeEnum.CERT_INFO_NOT_EXIST.getRespCode(), RespCodeEnum.CERT_INFO_NOT_EXIST.getRespDesc());
                }
                rglog.debug("证书存储类型<{}>", certInfo.getCertSaveType());
                String signData = null;
                //证书密钥串
                if ("0".equals(certInfo.getCertSaveType())) {
                    publicKey = RSAUtil.getPublicKeyFromBase64String(certInfo.getCertValue());
                } else if ("2".equals(certInfo.getCertSaveType())) {
                    if (certInfo.getCertSavePath().endsWith(".pfx")) {
                        List<String> aliasList = RSAUtil.getCertAliasFromPfxCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword());
                        rglog.debug("证书别名<{}>", aliasList.iterator().next());
                        publicKey = RSAUtil.getPublicKeyFromPfxCertFile(certInfo.getCertSavePath(), aliasList.get(0), certInfo.getCertPassword());
                    } else {
                        publicKey = RSAUtil.getPublicKeyFromCertFile(certInfo.getCertSavePath());
                    }
                }
                rglog.debug("验证签名报文signature = <{}>", signature);
                if (verifySignature(recvBuf, StringUtil.hexStringToBinary(signature), publicKey, "SHA1WithRSA")) {
                    rglog.debug("验签成功");
                    return recvBuf;
                } else {
                    rglog.error("验签失败");
                    return new byte[0];
                }
            }
        }

        return recvBuf;
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        //获取通道信息
        DstChannelCfgMapper dstChannelCfgMapper = new DstChannelCfgMapperImpl();
        DstChannelCfg dstChannelCfg = dstChannelCfgMapper.selectDstChannelCfgByPrimaryKey(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID), "CPCN", "NET");
        if (null == dstChannelCfg) {
            rglog.error("获取通道信息失败-->通道为<{}>", "CPCN");
            throw new BizException(RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespCode(), RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespDesc());
        }
        String secPlanId = dstChannelCfg.getSecPlanId();
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID), secPlanId);
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            xmlTreeUtil.setXmlTreeStringValue(CpcnTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
        }
        xmlTreeUtil.setXmlTreeStringValue(CpcnTreeNodeConstant.MESSAGE_HTTP_CONTENT_TYPE, CpcnParamConstant.CPCN_HTTP_CONTENT_TYPE);
        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/ArgList", "Content-Type");
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        rglog.debug("给根节点Request添加版本属性");
        //给根节点Request添加版本属性
        byte[] afterBytes = genRootNodeAttributes(bytes);

        String content = new String(afterBytes);
        StringBuilder sendBuild = new StringBuilder();
        rglog.debug("未签名的报文 = <{}>", content);
        if (!StringUtil.isEmptyOrNull(content)) {
            //获取加密证书
            String secPlanInfoListStr = xmlTreeUtil.getXmlTreeStringValue(CpcnTreeNodeConstant.SEC_PLAN_INFO_LIST);
            rglog.debug("安全计划信息列表  = <{}>", secPlanInfoListStr);
            List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListStr, SecPlanInfo.class);
            Optional<SecPlanInfo> secPlanInfoOptional = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(CpcnParamConstant.SIGN_CERT_TYPE)).findFirst();
            rglog.debug("获取证书索引<{}>", secPlanInfoOptional.get().getInstId() + secPlanInfoOptional.get().getSecPlanId() + secPlanInfoOptional.get().getSecPlanType());
            if (secPlanInfoOptional.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(secPlanInfoOptional.get().getInstId() + secPlanInfoOptional.get().getSecPlanId() + secPlanInfoOptional.get().getSecPlanType());
                if (null == certInfo) {
                    rglog.error("证书信息未配置,请先配置证书");
                    throw new BizException(RespCodeEnum.CERT_INFO_NOT_EXIST.getRespCode(), RespCodeEnum.CERT_INFO_NOT_EXIST.getRespDesc());
                }
                rglog.debug("证书存储类型<{}>", certInfo.getCertSaveType());
                String signData = null;
                //证书密钥串
                if ("0".equals(certInfo.getCertSaveType())) {
                    RSAPrivateKey privateKey = RSAUtil.getPrivateKeyFromBase64String(certInfo.getCertValue());
                    signData = generateSignString(content.getBytes("UTF-8"), privateKey, "SHA1withRSA");
                } else if ("2".equals(certInfo.getCertSaveType())) {
                    List<String> aliasList = RSAUtil.getCertAliasFromPfxCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword());
                    rglog.debug("证书别名<{}>", aliasList.iterator().next());
                    RSAPrivateKey privateKey = RSAUtil.getPrivateKeyFromPfxCertFile(certInfo.getCertSavePath(), aliasList.get(0), certInfo.getCertPassword());
                    signData = generateSignString(content.getBytes("UTF-8"), privateKey, "SHA1withRSA");
                }
                rglog.debug("签名后报文signData=<{}>", signData);

                sendBuild.append("message=");
                sendBuild.append(URLEncoder.encode(Base64.encodeBase64String(content.getBytes()), "UTF-8"));
                sendBuild.append("&signature=");
                sendBuild.append(URLEncoder.encode(signData, "UTF-8"));
                return sendBuild.toString().getBytes();
            }
        }

        return afterBytes;
    }

    private byte[] genRootNodeAttributes(byte[] bytes) throws DocumentException {
        SAXReader reader = new SAXReader();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        Document document = reader.read(byteArrayInputStream);
        Element rootElement = document.getRootElement();
        rglog.debug("根节点名称 = <{}>", rootElement.getName());//根节点Service
        //给根节点添加属性值
        rootElement.addAttribute("version", "2.0");
        rglog.debug("after document = <{}>", document.asXML());
        return document.asXML().getBytes();
    }

    private String generateSignString(byte[] signBlock, RSAPrivateKey privateKey, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature signature = Signature.getInstance(algorithm);
        signature.initSign(privateKey);
        signature.update(signBlock);
        return StringUtil.binaryToHexString(signature.sign());
    }

    private boolean verifySignature(byte[] recvBuf, byte[] sign, RSAPublicKey publicKey, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature signature = Signature.getInstance(algorithm);
        signature.initVerify(publicKey);
        signature.update(recvBuf);
        return signature.verify(sign);
    }
}
